Spring-AOP
撰写时间: 2020-01-12 总共: 2766 字 转载请注明: BY-SA 4.0(除特别声明或转载文章外)
Spring AOP
Spring AOP是Spring里非常重要的功能模块之一,提供了面向切面编程实现,面向切面编程在事务处理、日志记录、安全控制等方面广泛使用。
AOP概述
AOP(Aspect Oriented Programming)面向切面编程,与 OOP(Object Oriented Programming)面向对象编程相辅相成,提供了与OOP不同的抽象软件结构视角。
在OOP中通过封装继承等方式重用代码,但是还是会存在同样的代码分散在不同的地方,为了解决这些问题。AOP采取横向抽取机制,将分散的重复代码提取出来,在程序编译或运行阶段将这些重复代码应用到需要的地方。
AOP术语
-
切面(Aspect)
指代封装横切到系统功能(如事务处理)的类
-
连接点(Joinpoint)
指程序运行中的一些时间点,如方法调用或异常抛出
-
切入点(Pointcut)
指需要处理的连接点,在Spring AOP中,所有方法执行都是连接点,切入点只是一个描述信息,修饰连接点,通过切入点确定那些连接点需要被处理。
-
通知(Advice)
由切面添加到特定连接点的一段代码,是切面开启后切面的方法,是切面的具体实现。
(增强处理的功能方法)
-
引入(Introduction)
允许在现有的实现类中添加自定义的方法和属性
-
目标对象(Target Object)
所有被通知的对象
-
代理(Proxy)
是通知应用到目标对象之后被动态创建的对象
-
织入(Weaving)
是将切面代码插入到目标对象上,从而生成代理对象的过程
3种AOP织入方式
- 编译期织入,需要特殊的Java编译器
- 类装载期织入,需要特殊的类装载器
- 动态代理织入,(Spring AOP 默认方式),在运行期给目标类添加通知生成子类的方式。
AspectJ(采用编译期和类装载器织入)
动态代理
Java中有多种动态代理技术:JDK、CGLIB、Javassist、ASM
常用JDK与CGLIB(Spring AOP中也是)
JDK动态代理
JDK动态代理必须借助一个接口才能产生代理对象,在Spring中使用业务接口的类默认使用该方式进行AOP实现。
- 创建切面类(包含多个通知)
- 创建代理类(必须实现InvocationHandler接口,编写代理方法)
CGLIB动态代理
对于没有提供接口的类,采用CGLIB(Code Generation Library)动态代理。
CGLIB是一个高性能开源的代码生成包,采用非常底层的字节码技术,对目标类生成一个子类,对子类进行功能增强。
- 创建切面类
- 创建代理类(实现MethodInterceptor接口)
AOP实现
基于代理类的AOP实现
Spring默认使用JDK动态代理,ProxyFactoryBean创建代理是Spring AOP实现的最基本方式。
在Spring中通知在目标类方法中的连接点位置,可以分为6中通知类型:
-
环绕通知
目标方法执行前后的实施增强
-
前置通知
目标方法执行前的实施增强
-
后置返回通知
目标方法 成 功 执行后实施增强
-
后置通知
目标方法执行后实施增强(不管异常与否都执行,可用以释放资源)
-
异常通知
目标方法抛出异常后实施增强
-
引入通知
在目标类中添加新的方法和属性
ProxyFactoryBean负责为其他Bean实例创建代理实例,
<beans>
<!-- 其他bean 切面bean 目标对象bean等省略-->
<!-- ProxyFactoryBean -->
<bean id="testDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="实现的接口类"/>
<property name="target" ref="引用的目标对象的bean"/>
<property name="interceptorNames" value="需要织入的切面类bean"/>
<!--指定代理方式,默认为false为JDK动态代理,true为CGLIB动态代理-->
<property name="proxyTargetClass" value="true"/>
</bean>
</beans>
AspectJ开发
AspectJ是一个Java语言的 AOP框架。Spring 2.0 后引入AspectJ的支持,建议往后使用AspectJ实现Spring AOP。
基于XML配置
通过XML配置切面,切入点,通知等
applicationContext.xml
<!--beans-->
...
<!--AOP 配置-->
<aop:config>
<aop:aspect ref="切面类">
<!-- expression=execution(* 包名.*.*(..)) 指定该包下所有类型所有类名所以方法任意参数-->
<aop:pointcut expression="指定要增强那些方法"/>
<aop:before method="前置通知的方法实现" pointcut-ref="关联的切入点"/>
<aop:after-returning method="后置返回通知" pointcut-ref=""/>
<aop:around method="环绕" pointcut-ref=""/>
<aop:after-throwing method="异常" pointcut-ref=""/>
<aop:after method="后置" pointcut-ref=""/>
<aop:declare-parents types-matching="" implement-interface="" delegate-ref=""/>
</aop:aspect>
</aop:config>
繁杂!
基于注解
推荐使用注解方式
@Aspect
@Pointcut
@Before
@AfterReturning
@Around
@AfterThrowing
@After
-
创建切面类
编写好相应注解
-
创建配置文件
扫描包让注解生效等
启动基于注解的AspectJ支持
<beans>
<!--指定需要扫描的包-->
<context:component-scan base-package=""/>
<!--启动基于注解的AspectJ支持-->
<aop:aspectj-autoproxy/>
</beans>